/*
Created on Sept. 28th 2005
(C) 2005 
@author Michael J Ocean

This program is free software; you can redistribute it and/or
modify it under the terms of Version 2 of the GNU General Public 
License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package sxe.sensors;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;

import org.w3c.dom.Element;
import snbenchlib.XML;
import step.parse.SyntaxError;

public class SensorDetector {
	private static final String SENSOR_CONFIG_FILE_PATH = "sxe.sensorconfig.xml";
	private static final String SENSOR_CONFIG_ROOT_TAG = "sensorconfig";
	
	static private Hashtable<String,GenericSensor> m_htSensors;
	static private Hashtable<String,GenericSensor> m_htImageSensors;
	static private Hashtable<String,GenericSensor> m_htPTZImageSensors;
	static private Hashtable<String,GenericSensor> m_htTempSensors;
	static private Hashtable<String,GenericSensor> m_htWifiSensors;
	static private Hashtable<String,GenericSensor> m_htClockSensors;
	
	public SensorDetector() {
		super();
		m_htSensors = null;
		m_htImageSensors = null;
		m_htPTZImageSensors = null;
		m_htTempSensors = null;
		m_htWifiSensors = null;
		m_htClockSensors = null;
	}
	
	static public void Detect(){
		// load the XML sensor config file
		File f = new File(SENSOR_CONFIG_FILE_PATH);
		Element eSensorConfigRoot = null;
		if(f.exists()){
			try {
				eSensorConfigRoot = XML.parseXMLAndReturnRootElement(f, SENSOR_CONFIG_ROOT_TAG);
			} catch (NullPointerException e) {
				e.printStackTrace();
			} catch (SyntaxError e) {
				e.printStackTrace();
			}
		}
		if(eSensorConfigRoot==null){
			System.err.println("Sensor configuration file could not be located at: " + SENSOR_CONFIG_FILE_PATH);
			System.err.println("No optional sensors will be included in this SXE");
		}
		/* modify when adding new sensors */
		m_htClockSensors = new Hashtable<String, GenericSensor>();
		ClockSensor cs = new ClockSensor();
		cs.initializeSensor();
		m_htClockSensors.put(cs.getID(), cs);
		
		m_htImageSensors = ImageSensorDetector.detect();
		m_htTempSensors = TempSensorDetector.detect(eSensorConfigRoot);
		m_htWifiSensors = KismetSensorDetector.detect(eSensorConfigRoot);
		m_htPTZImageSensors = PTZSensorDetector.detect(eSensorConfigRoot);
				
		m_htSensors = new Hashtable<String,GenericSensor>();
		m_htSensors.putAll(m_htImageSensors);	
		m_htSensors.putAll(m_htTempSensors);
		m_htSensors.putAll(m_htWifiSensors);
		m_htSensors.putAll(m_htPTZImageSensors);
		m_htSensors.putAll(m_htClockSensors);
}
	
	static public boolean validateSensorType(String sType){
		return (getHashByFilter(sType)!=null);
	}
	
	private static Hashtable<String,GenericSensor> getHashByFilter(String filter) {
		if(filter.equalsIgnoreCase(GenericSensor.TYPE_ALL))
			return m_htSensors;
		if(filter.equalsIgnoreCase(GenericSensor.TYPE_IMAGE))
			return m_htImageSensors;
		if( filter.equalsIgnoreCase(GenericSensor.TYPE_TEMPERATURE ) )
		    return m_htTempSensors;
		if( filter.equalsIgnoreCase(GenericSensor.TYPE_KISMET ) )
			return m_htWifiSensors;
		if( filter.equalsIgnoreCase(GenericSensor.TYPE_IMAGE_PTZ ) )
			return m_htPTZImageSensors;
		if( filter.equalsIgnoreCase(GenericSensor.TYPE_CLOCK ) )
			return m_htClockSensors;		
		return null;
	}
	
	static public GenericSensor GetDevice(String sID){
		return GetDevice(sID,GenericSensor.TYPE_ALL);
	}
	
	static public GenericSensor GetDevice(String sID, String sType){
		Hashtable<String,GenericSensor> ht = getHashByFilter(sType);
		if(ht==null)
			return null;
				
		if(sID.equals("ANY")){
			if(ht.elements().hasMoreElements())
				return ht.elements().nextElement();
			return null;
		}
		else{
			 return ht.get(sID);
		}		
	}
	
	static public void ReleaseAll(){
		Collection c = m_htSensors.values();
		GenericSensor gs = null;
		Iterator it = c.iterator();
		while(it.hasNext()){
			gs = (GenericSensor)it.next();
			gs.shutDown();
		}
	}

	/**
	 * returns an ArrayList of all sensors connected to the SXE,
	 * send in a param of GenericSensor.TYPE_* to filter the 
	 * return type
	 * 
	 * @param filter one of GenericSensor.TYPE_*
	 * 
	 * @return ArrayList of GenericSensors of type specified by filter
	 */
	static public ArrayList<GenericSensor> getSensors(String filter){
		Hashtable m_htFiltered = getHashByFilter(filter);

		ArrayList<GenericSensor> al = new ArrayList<GenericSensor>();
		
		if(m_htFiltered!=null){
			Enumeration e = m_htFiltered.elements();
			while(e.hasMoreElements()){
				GenericSensor gs = (GenericSensor)e.nextElement();
				al.add(gs);
			}
		}
		return al;
	}
	
	/** returns structured XML for all sensors connected to the SXE and basic/high-level 
	 * information about them (JMF name and state).  */
	static public String getSensorsXML(String filter){
		Hashtable m_htFiltered = getHashByFilter(filter);
		
		String b = "<sensorDevices " +
				"xmlns=\"" + XML.XMLNS + "\">\n";
		if(m_htFiltered!=null){
			Enumeration e = m_htFiltered.elements();
			while(e.hasMoreElements()){
				GenericSensor gs = (GenericSensor)e.nextElement();
				b+=gs.GetInfoXML();
			}
		}
		b += "</sensorDevices>\n";
		return b;
	}

	/** returns structured XML displaying all sensor types connected 
	 *  to (controlled by) this SXE */
	static public String getSensorTypesXML(){		
		String b = "<sensorTypes " +
				"xmlns=\"" + XML.XMLNS + "\">\n";
		if(m_htSensors!=null){
			if(m_htClockSensors!=null){
				b+="<clockSensors " +
						"id=\"" + GenericSensor.TYPE_CLOCK + "\" " + 
						"xmlns=\"" + XML.XMLNS + "\">\n";
				b+=	XML.tagger(4,"count", String.valueOf(m_htClockSensors.size()));
				b+="</clockSensors>";
			}
						
			if(m_htImageSensors!=null){
				b+="<imageSensors " +
						"id=\"" + GenericSensor.TYPE_IMAGE + "\" " + 
						"xmlns=\"" + XML.XMLNS + "\">\n";
				b+=	XML.tagger(4,"count", String.valueOf(m_htImageSensors.size()));
				b+="</imageSensors>";
			}

			if(m_htPTZImageSensors!=null){
				b+="<ptzImageSensors " +
						"id=\"" + GenericSensor.TYPE_IMAGE_PTZ + "\" " + 
						"xmlns=\"" + XML.XMLNS + "\">\n";
				b+=	XML.tagger(4,"count", String.valueOf(m_htPTZImageSensors.size()));
				b+="</ptzImageSensors>";
			}

			if( m_htTempSensors != null ) {
			    b += "<tempSensors " +
				"id=\"" + GenericSensor.TYPE_TEMPERATURE + 
				"\" " + "xmlns=\"" + XML.XMLNS + "\">\n";
			    b += XML.tagger( 4, "count", String.valueOf( 
						 m_htTempSensors.size() ) );
			    b += "</tempSensors>";
			}
			if( m_htWifiSensors != null ) {
			    b += "<wifiSensors " +
				"id=\"" + GenericSensor.TYPE_KISMET + 
				"\" " + "xmlns=\"" + XML.XMLNS + "\">\n";
			    b += XML.tagger( 4, "count", String.valueOf( 
						 m_htWifiSensors.size() ) );
			    b += "</wifiSensors>";
			}
		}
		b += "</sensorTypes>\n";
		return b;
	}
	
	// ID registry - static functions/data
    private static HashMap<String,String> idmap;
    private static int idcounter;
    private static void InitID() {
	if (idmap == null) {
	    idmap = new HashMap<String,String>();
	    idcounter = 1;
	}
    }
    public static String ProduceID() {
		InitID();
		String newkey;
		do {
		    newkey = new Integer(idcounter).toString();
		    idcounter++;
		} while (idmap.containsKey(newkey));
		return newkey;
    }
    public static void RegisterID(String id) {
		InitID();
		idmap.put(id,id);
    }

    /* methods below are for SSD use, primarily */
    /**
     * Initialize the Sensor repository -- SXEs call Detect and thus
     * don't need to call this method.  The SSD does, however.
     */
    public static void Init(){
    	m_htClockSensors = new Hashtable<String, GenericSensor>();
    	m_htImageSensors = new Hashtable<String, GenericSensor>();
    	m_htPTZImageSensors = new Hashtable<String, GenericSensor>();
    	m_htTempSensors = new Hashtable<String, GenericSensor>();
    	m_htWifiSensors = new Hashtable<String, GenericSensor>();
    	m_htSensors = new Hashtable<String, GenericSensor>();
    }
    
    /**
     * Returns true if added.  Returns false if the item was already there
     * Either way, the sensor is present after the call.
     * 
     * @param gs
     * @return
     */
    public static boolean AddSensor(GenericSensor gs){
    	Hashtable<String,GenericSensor> ht = getHashByFilter(gs.getType());
    	if(ht.containsKey(gs.getID()))
    		return false;
    	ht.put(gs.getID(), gs);
    	m_htSensors.put(gs.getID(), gs);
    	return true;
    }
    
    /**
     * Returns true if removed.  Returns false if the item was not there
     * to be removed.  Eitherway, the sensor is gone after the call.
     * 
     * @param gs
     * @return
     */
    public static boolean RemoveSensor(GenericSensor gs){
    	Hashtable<String,GenericSensor> ht = getHashByFilter(gs.getType());
    	if(ht.containsKey(gs.getID()))
    		return false;
    	ht.put(gs.getID(), gs);
    	m_htSensors.put(gs.getID(), gs);
    	return true;
    }
}
